use proc_macro2::TokenStream;
use quote::{format_ident, quote};



#[derive(Debug)]
pub struct Config {
    dma: u8,
    stream: u8,
    ch: u8,
    size: u8,
}

impl Config {

    // DMA1_STREAM0_CH0_SIZE8
    pub fn parse(attr: &str) -> Self {
        let attrs: Vec<&str> = attr.split("_").collect();
        let dma = attrs[0];
        let stream = attrs[1];
        let ch = attrs[2];
        let size = attrs[3];
        let dma = &dma[3..];
        let dma: u8 = dma.parse().unwrap();
        let stream = &stream[6..];
        let stream: u8 = stream.parse().unwrap();
        let ch = &ch[2..];
        let ch: u8 = ch.parse().unwrap();
        let size = &size[4..];
        let size: u8 = size.parse().unwrap();
        Self { dma, stream, ch, size }
    }

    pub fn write_with_it(&self) -> TokenStream {
        let size_type = format_ident!("u{}", self.size);
        let dma = format_ident!("DMA{}", self.dma);
        let bits = format_ident!("bits{}", self.size);
        let stream = self.stream as usize;
        let ch = self.ch;

        quote! {
            #[inline(always)]
            pub unsafe fn write_with_it(&self, periph_addr: u32, buf: &[#size_type]) {
                #dma.st[#stream].cr.reset();
                #dma.st[#stream].par.write(|w| {
                    w.pa().variant(periph_addr)
                });
                #dma.st[#stream].m0ar.write(|w| w.m0a().variant(buf.as_ptr() as u32));
                #dma.st[#stream].ndtr.write(|w| {
                    w.ndt().variant(buf.len() as u16)
                });
                #dma.st[#stream].cr.write(|w| {
                    w.chsel().variant(#ch)
                    .msize().#bits()
                    .psize().#bits()
                    .minc().incremented()
                    .dir().memory_to_peripheral()
                    .tcie().enabled()
                    .en().enabled()
                });
            }
        }
    }

    pub fn read_with_circ(&self) -> TokenStream {
        let size_type = format_ident!("u{}", self.size);
        let dma = format_ident!("DMA{}", self.dma);
        let bits = format_ident!("bits{}", self.size);
        let stream = self.stream as usize;
        let ch = self.ch;
        quote! {
            #[inline(always)]
            pub unsafe fn read_with_circ(&self, periph_addr: u32, buf: &mut[#size_type]) {
                #dma.st[#stream].cr.reset();
                #dma.st[#stream].par.write(|w| {
                    w.pa().variant(periph_addr)
                });
                #dma.st[#stream].m0ar.write(|w| w.m0a().variant(buf.as_ptr() as u32));
                #dma.st[#stream].ndtr.write(|w| {
                    w.ndt().variant(buf.len() as u16)
                });
                #dma.st[#stream].cr.write(|w| {
                    w.chsel().variant(#ch)
                    .msize().#bits()
                    .psize().#bits()
                    .minc().incremented()
                    .circ().enabled()
                    .dir().peripheral_to_memory()
                    .en().enabled()
                });
            }
        }
    }

    pub fn init_rcc(&self) -> TokenStream {
        let dma = format_ident!("dma{}en", self.dma);
        quote! {    
            #[inline(always)]
            pub fn init_rcc(&self) {
                RCC.ahb1enr.modify(|_, w| {
                    w.#dma().enabled()
                });
            }
        }
    }

    pub fn is_complete(&self) -> TokenStream {
        let dma = format_ident!("DMA{}", self.dma);
        let sr = if self.stream > 3 {
            format_ident!("hisr")
        } else {
            format_ident!("lisr")
        };
        let tcif = format_ident!("tcif{}", self.stream);
        quote! {
            #[inline(always)]
            pub fn is_complete(&self) -> bool {
                #dma.#sr.read().#tcif().is_complete()
            }
        }
    }

    pub fn ndtr(&self) -> TokenStream {
        let dma = format_ident!("DMA{}", self.dma);
        let stream = self.stream as usize;
        quote! {
            #[inline(always)]
            pub fn ndtr(&self) -> u16 {
                #dma.st[#stream].ndtr.read().ndt().bits()
            }
        }
    }

    pub fn clear_flags(&self) -> TokenStream {
        let dma = format_ident!("DMA{}", self.dma);
        let fcr = if self.stream > 3 {
            format_ident!("hifcr")
        } else {
            format_ident!("lifcr")
        };
        let cfeif = format_ident!("cfeif{}", self.stream);
        let cdmeif = format_ident!("cdmeif{}", self.stream);
        let cteif = format_ident!("cteif{}", self.stream);
        let chtif = format_ident!("chtif{}", self.stream);
        let ctcif = format_ident!("ctcif{}", self.stream);
        quote! {
            #[inline(always)]
            pub fn clear_flags(&self) {
                #dma.#fcr.write(|w| {
                    w.#cfeif().clear()
                    .#cdmeif().clear()
                    .#cteif().clear()
                    .#chtif().clear()
                    .#ctcif().clear()
                });
            }
        }
    }

}











